package com.liuguang.gateway.config.security;

import com.liuguang.common.annotations.AnyoneCanAccess;
import com.liuguang.gateway.config.security.filter.JwtFilter;
import com.liuguang.gateway.config.security.others.JwtAuthenticationEntryPoint;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.SecurityBuilder;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.authentication.configuration.EnableGlobalAuthentication;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.WebSecurityConfigurer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;

import java.util.HashSet;
import java.util.Map;

/**
 * @ClassName: SecurityConfig
 * @Description：
 * @Author: wanghr
 * Date: 4:50 PM 2020/9/11
 * Version: 1.0
 */
@Configuration
@EnableWebSecurity
//@EnableGlobalAuthentication() todo: 待验证注解作用
//允许使用 @PreAuthorize 注解、@Secured 注解
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    private static final Logger logger = LoggerFactory.getLogger(SecurityConfig.class);

    @Value("${security.permitUrl}")
    private String permitUrl;
    //保存 注解允许路径
    private HashSet<String> urlSet = new HashSet<>();

    @Autowired
    private ApplicationContext applicationContext;

    @Autowired
    private JwtFilter jwtFilter;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        getAnnotationUrl();
        logger.info("urlSet:{}", urlSet);
        logger.info("使用自定义security配置");
        http
                .exceptionHandling()
                .authenticationEntryPoint(new JwtAuthenticationEntryPoint())
                .and()
                //在过滤器链中去除 csrf 过滤器，默认情况下是不允许 post 方式请求的
                .csrf().disable()
                .authorizeRequests()
                //配置文件 配置允许访问路径
                .antMatchers(permitUrl.split(",")).permitAll()
                //注解 配置允许访问路径
                .antMatchers(urlSet.toArray(new String[0])).permitAll()
                .anyRequest().authenticated()
                .and()
                .formLogin().and()
                .httpBasic();
        http.addFilterBefore(jwtFilter, UsernamePasswordAuthenticationFilter.class);
    }

    private void getAnnotationUrl() {
        Map<RequestMappingInfo, HandlerMethod> handlerMethods = applicationContext.getBean(RequestMappingHandlerMapping.class).getHandlerMethods();

        handlerMethods.entrySet().forEach((entry) -> {
            HandlerMethod handlerMethod = entry.getValue();
            AnyoneCanAccess methodAnnotation = handlerMethod.getMethodAnnotation(AnyoneCanAccess.class);
            if (methodAnnotation != null) {
                urlSet.addAll(entry.getKey().getPatternsCondition().getPatterns());
            }
        });
    }


    @Bean
    public BCryptPasswordEncoder getBCryptPasswordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Bean
    @Override
    protected AuthenticationManager authenticationManager() throws Exception {
        return super.authenticationManager();
    }


}
